
use std::sync::mpsc::channel;

use clap::Args;
use serde::{Deserialize, Serialize};
use wtask_base::{error::WResult, export::log_info, logs::log_format, tcp::{WTcpStream, stream_write_data, stream_write_raw}};

use crate::{task::utils::meta::{CmdArgs, CmdRet, TaskStatus}, utils::{AppTrait, StreamStatus}};



#[derive(Debug, Clone, Serialize, Deserialize, Args)]
pub struct AppCmd {
    #[command(flatten)]
    pub(crate) data: CmdArgs,

    /// 不等待程序结束，否则立即返回程序运行状态
    #[arg(long)]
    pub no_wait: bool,
}

impl From<CmdArgs> for AppCmd {
    fn from(value: CmdArgs) -> Self {
        Self { data: value, no_wait: false }
    }
}

impl AppCmd {
    pub async fn server(self, exe: String, session_token: &str, stream_session: &WTcpStream) -> WResult<()> {
        let note = self.data.note.clone();
        let (rx_log, tx_log) = channel::<String>();
        let cmd_ret = self.data.run_cmd_task(exe, Box::new(move | _, v | { let _ = rx_log.send(v); }))?;
        log_info!("Run: {} {} ...", cmd_ret.basic.id, cmd_ret.basic.exe);
        stream_write_data(&cmd_ret.basic, stream_session.1.as_ref(), session_token).await?;
        if !self.no_wait {
            while let Ok(log_temp) = tx_log.recv() {
                stream_write_raw(stream_session.1.as_ref(), log_temp.as_bytes(), session_token).await?;
            }
            let log_final = match cmd_ret.handle.join() {
                Ok(Ok(v)) if v.status == TaskStatus::Success => {
                    log_format(format!("{:?} success", note))
                },
                Ok(Ok(_)) => {
                    log_format(format!("{:?} run error", note))
                },
                Ok(Err(e)) => {
                    log_format(format!("{:?} run error: {}", note, e))
                },
                Err(e) => {
                    log_format(format!("{:?} start error: {:?}", note, e))
                },
            };
            stream_write_raw(stream_session.1.as_ref(), log_final.as_bytes(), session_token).await?;
        }
        Ok(())
    }
}

impl AppTrait for AppCmd {
    type Status = ();
    type Res = CmdRet;

    async fn client_handle<M: FnMut(bool, String) + Send + Sync>(self, stream: &WTcpStream, key: &str, msg_func: M, _status: Option<Self::Status>) -> WResult<Self::Res> {
        Self::client_wait_log(stream, key, msg_func).await
    }

    async fn server_handle(self, _server_token: &str, session_token: &str, stream_session: &WTcpStream, _stream_status: StreamStatus) -> WResult<()> {
        let exe_temp = self.data.cmd[0].split_once(" ").unwrap_or((&self.data.cmd[0], "")).0.to_string();
        self.server(exe_temp, session_token, stream_session).await
    }
}

